package com.ts.manage_media.service;

import com.alibaba.fastjson.JSON;
import com.ts.framework.domain.media.MediaFile;
import com.ts.framework.domain.media.response.CheckChunkResult;
import com.ts.framework.domain.media.response.MediaCode;
import com.ts.framework.exception.ExceptionCast;
import com.ts.framework.model.response.CommonCode;
import com.ts.framework.model.response.ResponseResult;
import com.ts.manage_media.config.RabbitMQConfig;
import com.ts.manage_media.dao.MediaFileRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

@Service
@Slf4j
public class MediaUploadService {
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    MediaFileRepository mediaFileRepository;
    //检查文件是否已经存在,如果不存在,创建文件存放目录
    /**
     * 根据文件md5得到文件路径 i/o/iopuerwqyto2222222222223/chunk/
     * i/o/iopuerwqyto2222222222223/iopuerwqyto2222222222223.mp4
     * F:/develop/video/i/o/iopuerwqyto2222222222223/iopuerwqyto2222222222223.mp4
     * 规则：
     * 一级目录：md5的第一个字符
     * 二级目录：md5的第二个字符
     * 三级目录：md5
     * 文件名：md5+文件扩展名
     * @param fileMd5 文件md5值
     * @param fileExt 文件扩展名
     * @return 文件路径
     */
    @Value("${ts-service-manage-media.upload-location}")
    private String uploadPath;
    //得到文件目录相对路径，路径中去掉根目录
    @Value("${ts-service-manage-media.mq.routingkey-media-video}")
    private String routingkeyMediaVideo;

    private String getFileForderPath(String fileMd5) {
        return uploadPath + fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/";
    }

    private String getFileChunkForderPath(String fileMd5) {
        return uploadPath + fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/chunk/";
    }

    //文件上传注册
    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //在目录中监测文件是否存在
        String filePath = this.getFileForderPath(fileMd5) + fileMd5 + "." + fileExt;
        File file = new File(filePath);
        //判断文件是否存在
        if (file.exists()) {
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        } else {
            String fileForderPath = this.getFileForderPath(fileMd5);
            File file1 = new File(fileForderPath);
            if (!file1.exists()) {
                file1.mkdirs();
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }


    //判断块是否存在
    public CheckChunkResult checkchunk(String fileMd5, Integer chunk, Integer chunkSize) {

        String chunkFileForderPath = this.getFileChunkForderPath(fileMd5);
        File file = new File(chunkFileForderPath);
        if (!file.exists()) {
            file.mkdirs();
            return new CheckChunkResult(CommonCode.SUCCESS, false);
        } else {
            File file1 = new File(chunkFileForderPath + chunk);
            if (file1.exists()) {
                return new CheckChunkResult(CommonCode.SUCCESS, true);
            }
        }
        return new CheckChunkResult(CommonCode.SUCCESS, false);
    }

    //分块上传
    public ResponseResult uploadchunk(MultipartFile file, Integer chunk, String fileMd5) {

        FileOutputStream fileOutputStream = null;
        InputStream inputStream = null;
        try {
            String fileChunkForderPath = this.getFileChunkForderPath(fileMd5);
            File file1 = new File(fileChunkForderPath + chunk);
            //创建输出流
            fileOutputStream = new FileOutputStream(file1);
            //输入流
            inputStream = file.getInputStream();
            IOUtils.copy(inputStream, fileOutputStream);
        } catch (IOException e) {
            log.error(e.getMessage());
            e.printStackTrace();
            ExceptionCast.cast(CommonCode.FAIL);
        } finally {
            try {
                fileOutputStream.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                log.error(e.getMessage());
                ExceptionCast.cast(CommonCode.FAIL);
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    //合并分块
    public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //1、合并分块
        File mergeFile = mergeFile(fileMd5, fileExt);

        // String fileMergePath = this.getFileForderPath(fileMd5)+fileMd5+"."+ fileExt;
        //2、校验合并之后的资源md5和原始资源fileMd5是否一致
        boolean flag = mergechunkFile(mergeFile, fileMd5);

        //3、把资源信息保存到mongoDb数据库中
        if (flag) {
            MediaFile mediaFile = new MediaFile();
            mediaFile.setFileId(fileMd5);
            mediaFile.setFileName(fileMd5 + "." + fileExt);
            mediaFile.setFileOriginalName(fileName);
            mediaFile.setFilePath(fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/");
            mediaFile.setFileSize(fileSize);
            mediaFile.setFileStatus("301002");//
            mediaFile.setFileType(fileExt);
            // mediaFile.setFileUrl(fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/"+fileMd5+"."+ fileExt);
            mediaFileRepository.save(mediaFile);
            String fileChunkForderPath = this.getFileChunkForderPath(fileMd5);
            File file = new File(fileChunkForderPath);
            File[] files = file.listFiles();
            for (File f : files) {
                f.delete();
            }
            file.delete();

            //发送 消息
            ResponseResult responseResult = sendMsg(mediaFile.getFileId());
            return responseResult;
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    private ResponseResult sendMsg(String mediaId) {
        Map<String, String> map = new HashMap<>();
        map.put("mediaId", mediaId);
        String jsonString = JSON.toJSONString(map);
        rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK, routingkeyMediaVideo, jsonString);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    private boolean mergechunkFile(File mergeFile, String fileMd5) {
        //2、校验合并之后的资源md5和原始资源fileMd5是否一致
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(mergeFile);
            String md5Hex = DigestUtils.md5Hex(fileInputStream);
            if (!md5Hex.equalsIgnoreCase(fileMd5)) {
                ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
        } finally {
            try {
                fileInputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    private File mergeFile(String fileMd5, String fileExt) {
        String fileChunkForderPath = this.getFileChunkForderPath(fileMd5);
        File chunkFile = new File(fileChunkForderPath);
        File[] listFiles = chunkFile.listFiles();
        List<File> files = Arrays.asList(listFiles);

        //排序
        Collections.sort(files, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName())) {
                    return 1;
                }
                return -1;
            }
        });

        String fileMerge = getFileForderPath(fileMd5) + fileMd5 + "." + fileExt;
        File file1 = new File(fileMerge);
        RandomAccessFile rw = null;
        try {
            rw = new RandomAccessFile(file1, "rw");
            rw.seek(0);
            byte[] bytes = new byte[1024];

            for (File file : files) {
                RandomAccessFile r = new RandomAccessFile(file, "r");
                int len = -1;
                while ((len = r.read(bytes)) != -1) {
                    rw.write(bytes, 0, len);
                }
                r.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        } finally {
            try {
                rw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return file1;
    }
}

